package com.carey.blog.event.sync;

import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_ARTICLE_ABSTRACT;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_ARTICLE_CATEGORIES;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_ARTICLE_CONTENT;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_ARTICLE_CREATE_DATE;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_ARTICLE_ID;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_ARTICLE_TITLE;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_BLOGGING_SYS_USER_NAME;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_EXTERNAL_BLOGGING_SYS_USER_PASSWORD;
import static com.carey.blog.model.BlogSync.BLOG_SYNC_MGMT_UPDATE_ENABLED;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.json.JSONException;
import org.json.JSONObject;

import com.carey.blog.event.EventTypes;
import com.carey.blog.framework.latke.Keys;
import com.carey.blog.framework.latke.event.AbstractEventListener;
import com.carey.blog.framework.latke.event.Event;
import com.carey.blog.framework.latke.event.EventException;
import com.carey.blog.framework.latke.event.EventManager;
import com.carey.blog.listener.CareyBlogServletListener;
import com.carey.blog.model.Article;
import com.carey.blog.repository.BlogSyncManagementRepository;
import com.carey.blog.repository.ExternalArticleSoloArticleRepository;
import com.carey.blog.repository.impl.BlogSyncMgmtGAERepository;
import com.carey.blog.repository.impl.ExternalArticleSoloArticleGAERepository;
import com.carey.blog.sync.BlogFactory;
import com.carey.blog.sync.MetaWeblog;
import com.carey.blog.sync.MetaWeblogPost;
import com.carey.blog.sync.Post;
import com.carey.blog.sync.SyncException;
import com.carey.blog.util.Users;

/**
 * This listener is responsible for blog sync update article to external
 * blogging system.
 * 
 */
public abstract class AbstractUpdateArticleProcessor extends
		AbstractEventListener<JSONObject> {

	/**
	 * Logger.
	 */
	private static final Logger LOGGER = Logger
			.getLogger(AbstractUpdateArticleProcessor.class.getName());
	/**
	 * Blog sync management repository.
	 */
	private BlogSyncManagementRepository blogSyncManagementRepository = BlogSyncMgmtGAERepository
			.getInstance();
	/**
	 * External blog article-Solo article repository.
	 */
	private ExternalArticleSoloArticleRepository externalArticleSoloArticleRepository = ExternalArticleSoloArticleGAERepository
			.getInstance();
	/**
	 * User utilities.
	 */
	private Users userUtils = Users.getInstance();

	/**
	 * Constructs a {@link BlogSyncMgmtUpdateArticleProcessor} object with the
	 * specified event manager.
	 * 
	 * @param eventManager
	 *            the specified event manager
	 */
	public AbstractUpdateArticleProcessor(final EventManager eventManager) {
		super(eventManager);
	}

	/**
	 * Updates articles with the specified event.
	 * 
	 * @param event
	 *            the specified event
	 * @return event result, for example,
	 * 
	 *         <pre>
	 * {
	 *     "sc": "BLOG_SYNC_SUCC"
	 * }
	 * </pre>
	 * @throws EventException
	 *             event exception
	 * @throws SyncException
	 *             sync exception
	 */
	protected final JSONObject updateArticle(final Event<JSONObject> event)
			throws EventException, SyncException {
		final JSONObject eventData = event.getData();

		JSONObject article = null;
		try {
			article = eventData.getJSONObject(Article.ARTICLE);

			LOGGER.log(
					Level.FINER,
					"Processing an event[type={0}, data={1}] in listener[className={2}]",
					new Object[] { event.getType(), article,
							AbstractAddArticleProcessor.class.getName() });
		} catch (final JSONException e) {
			LOGGER.log(Level.SEVERE, e.getMessage(), e);
			throw new EventException(e);
		}

		final String externalBloggingSys = getExternalBloggingSys();
		try {
			final JSONObject ret = new JSONObject();
			final JSONObject blogSyncMgmt = blogSyncManagementRepository
					.getByExternalBloggingSystem(externalBloggingSys);
			if (null == blogSyncMgmt) {
				LOGGER.log(
						Level.FINER,
						"Not found syn management settings for external blogging system[{0}]",
						externalBloggingSys);
				ret.put(Keys.STATUS_CODE,
						BlogSyncStatusCodes.BLOG_SYNC_NO_NEED_TO_SYNC);

				return ret;
			}

			final boolean hasMultipleUsers = userUtils.hasMultipleUsers();
			if (hasMultipleUsers) {
				LOGGER.log(Level.FINER,
						"Disabled article sync management caused by has multiple users");
				ret.put(Keys.STATUS_CODE,
						BlogSyncStatusCodes.BLOG_SYNC_NO_NEED_TO_SYNC);
				return ret;
			}

			LOGGER.log(
					Level.FINER,
					"Got a blog sync management setting[{0}]",
					blogSyncMgmt
							.toString(CareyBlogServletListener.JSON_PRINT_INDENT_FACTOR));
			if (!blogSyncMgmt.getBoolean(BLOG_SYNC_MGMT_UPDATE_ENABLED)) {
				LOGGER.log(
						Level.INFO,
						"External blogging system[{0}] need NOT to syn update article",
						externalBloggingSys);
				ret.put(Keys.STATUS_CODE,
						BlogSyncStatusCodes.BLOG_SYNC_NO_NEED_TO_SYNC);

				return ret;
			} else {
				final String userName = blogSyncMgmt
						.getString(BLOG_SYNC_EXTERNAL_BLOGGING_SYS_USER_NAME);
				final String userPwd = blogSyncMgmt
						.getString(BLOG_SYNC_EXTERNAL_BLOGGING_SYS_USER_PASSWORD);
				final Post externalArticle = new MetaWeblogPost(article);
				final String articleId = article.getString(Keys.OBJECT_ID);
				LOGGER.log(Level.FINEST,
						"Getting External article-Solo article relation....");
				final JSONObject externalArticleSoloArticleRelation = externalArticleSoloArticleRepository
						.getBySoloArticleId(articleId, externalBloggingSys);
				if (null == externalArticleSoloArticleRelation) {
					// This article published without sync adding enabled.
					// See issue 72 for details
					ret.put(Keys.STATUS_CODE,
							BlogSyncStatusCodes.BLOG_SYNC_FAIL);

					return ret;
				}

				final String postId = externalArticleSoloArticleRelation
						.getString(BLOG_SYNC_EXTERNAL_ARTICLE_ID);
				final MetaWeblog metaWeblog = BlogFactory
						.getMetaWeblog(externalBloggingSys);
				metaWeblog.setUserName(userName);
				metaWeblog.setUserPassword(userPwd);
				metaWeblog.editPost(postId, externalArticle);

				externalArticleSoloArticleRelation.put(
						BLOG_SYNC_EXTERNAL_ARTICLE_ABSTRACT,
						article.getString(Article.ARTICLE_ABSTRACT));
				externalArticleSoloArticleRelation.put(
						BLOG_SYNC_EXTERNAL_ARTICLE_CATEGORIES,
						article.getString(Article.ARTICLE_TAGS_REF));
				externalArticleSoloArticleRelation.put(
						BLOG_SYNC_EXTERNAL_ARTICLE_CONTENT,
						article.getString(Article.ARTICLE_CONTENT));
				externalArticleSoloArticleRelation.put(
						BLOG_SYNC_EXTERNAL_ARTICLE_CREATE_DATE,
						article.get(Article.ARTICLE_CREATE_DATE));
				externalArticleSoloArticleRelation.put(
						BLOG_SYNC_EXTERNAL_ARTICLE_TITLE,
						article.getString(Article.ARTICLE_TITLE));
				externalArticleSoloArticleRepository.update(
						externalArticleSoloArticleRelation
								.getString(Keys.OBJECT_ID),
						externalArticleSoloArticleRelation);
			}

			ret.put(Keys.STATUS_CODE, BlogSyncStatusCodes.BLOG_SYNC_SUCC);

			return ret;
		} catch (final SyncException e) {
			LOGGER.log(Level.WARNING,
					"Can not update article sync, error msg[{0}]",
					e.getMessage());
			throw e;
		} catch (final Exception e) {
			LOGGER.log(Level.SEVERE,
					"Can not handle event[{0}], error msg[{1}]", new String[] {
							getEventType(), e.getMessage() });
			throw new EventException("Can not handle event[" + getEventType()
					+ "]");
		}
	}

	/**
	 * Gets the event type {@linkplain EventTypes#ADD_ARTICLE}.
	 * 
	 * @return event type
	 */
	@Override
	public final String getEventType() {
		return EventTypes.UPDATE_ARTICLE;
	}

	/**
	 * Gets the external blogging system name.
	 * 
	 * @return external blogging system name
	 * @see com.carey.blog.CareyBlogServletListener.SoloServletListener#SUPPORTED_BLOG_SYNC_MGMT_EXTERNAL_BLOGGING_SYSTEMS
	 */
	public abstract String getExternalBloggingSys();
}
